Android 布局优化

关于布局优化,可能大家都经常有听到,其实核心的就是两点:

  • 尽可能的让自己的布局简单起来;

  • 尽量让控件在用的时候再初始化;

针对这两点,今天我们来介绍一下Android 提供的三个用来优化布局的标签,分别是include、merge与ViewStub,其中ViewStub是动态加载视图到内存。

include

布局重用,比如在Android的应用程序开发中,标题栏是必不可少的一个元素,大部分页面都要用到,而且布局都是一样的,这时候使用include标签就显得极其的方便。使用时通常需要注意以下几点

  • include标签的layout_*属性会替换掉被include视图的根节点的对应属性。

  • include标签的id属性会替换掉被include视图的根节点id

  • 一个布局文件中支持include多个视图,但是这样会导致获取被include视图内的控件冲突

merge

减少视图节点,在Android中通过使用merge能够减少视图的节点数,从而减少视图在绘制过程消耗的时间,达到提高UI性能的效果。使用merge时通常需要注意以下几点:

  • merge必须放在布局文件的根节点上。

  • merge并不是一个ViewGroup,也不是一个View,它相当于声明了一些视图,等待被添加。

  • merge标签被添加到A容器下,那么merge下的所有视图将被添加到A容器下。

  • 因为merge标签并不是View,所以在通过LayoutInflate.inflate方法渲染的时候, 第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点。

  • 如果Activity的布局文件根节点是FrameLayout,可以替换为merge标签,这样,执行setContentView之后,会减少一层FrameLayout节点。

  • 自定义View如果继承LinearLayout,建议让自定义View的布局文件根节点设置成merge,这样能少一层结点。

  • 因为merge不是View,所以对merge标签设置的所有属性都是无效的。

ViewStub

ViewStub 直接继承自View,是一种不可见,0大小的可以在运行的时候再加载的View,仅只有在调用inflate()进行映射内容布局之后(值得注意的是ViewStub只能inflate一次,再次进行inflate的时候会报异常)或者设置为Visibility时才可见

适用场景

在程序的运行期间,某个布局在被Inflate后,就不会有变化,除非重新启动。因为ViewStub只能Inflate一次,inflate之后就不能直接使用ViewStub来控制布局。

示例

  • activity_viewstub_test02.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_vs_showView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="显示ViewStub"/>
<Button
android:id="@+id/btn_vs_changeHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="更改ViewStub"/>
<Button
android:id="@+id/btn_vs_hideView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_weight="1"
android:text="隐藏ViewStub"/>
</LinearLayout>


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="正在加载。。。"/>

<!--ViewStub 展示或者隐藏内容-->
<ViewStub
android:id="@+id/viewstub_test"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inflatedId="@+id/iv_VsContent"
android:layout="@layout/iv_vs_content"/>

</RelativeLayout>
  • ViewStubTestActivitiy.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* Created by CnPeng on 2017/1/11. ViewStub 的使用示例
*/

public class ViewStubTestActivitiy extends AppCompatActivity implements View.OnClickListener {

private ViewStub viewStub;
private TextView hintText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewstub_test02);

init();
}

/**
* 初始化
*/
private void init() {
viewStub = (ViewStub) findViewById(R.id.viewstub_test);

Button btn_show = (Button) findViewById(R.id.btn_vs_showView);
Button btn_hide = (Button) findViewById(R.id.btn_vs_hideView);
Button btn_change = (Button) findViewById(R.id.btn_vs_changeHint);

btn_show.setOnClickListener(this);
btn_hide.setOnClickListener(this);
btn_change.setOnClickListener(this);

}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_vs_showView:

//inflate 方法只能被调用一次,因为调用后viewStub对象就被移除了视图树;
// 所以,如果此时再次点击显示按钮,就会崩溃,错误信息:ViewStub must have a non-null ViewGroup viewParent;
// 所以使用try catch ,当此处发现exception 的时候,在catch中使用setVisibility()重新显示
try {
View iv_vsContent = viewStub.inflate(); //inflate 方法只能被调用一次,
hintText = (TextView) iv_vsContent.findViewById(R.id.tv_vsContent);
// hintText.setText("没有相关数据,请刷新");
} catch (Exception e) {
viewStub.setVisibility(View.VISIBLE);
} finally {
hintText.setText("没有相关数据,请刷新");
}
break;
case R.id.btn_vs_hideView: //如果显示
viewStub.setVisibility(View.INVISIBLE);
break;
case R.id.btn_vs_changeHint:
if (hintText!=null) {
hintText.setText("网络异常,无法刷新,请检查网络");
}
break;
}
}
}
  • iv_vs_content.xml –要通过ViewStub展示出来的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:src="@mipmap/ic_launcher"/>

<TextView
android:id="@+id/tv_vsContent"
android:layout_width="match_parent"
android:layout_height="@dimen/dp30"
android:gravity="center"
android:text="eeee"/>
</LinearLayout>

参考